/* @flow */

const validDivisionCharRE = /[\w).+\-_$\]]/

//0x27：单引号'
//0x22：双引号"
//0x60：反引号`（也就是最新的模板字符串的符号）
//0x2f：斜线/
//0x5C：反斜线\
//0x7C：竖线|

//注：对于"\\"这样的转义字符，处理时，就是只有一个反斜线\，两个反斜线只是在程序写的时候。

//该方法用于解析过滤器，过滤器可以用在两个地方：mustache 插值和 v-bind 表达式，
//详情可参考官方文档

//实例为：<div :id="name | capitalize('james', lastName)"></div>
//则id属性的解析结果为_f("capitalize")(name,'james', lastName)
export function parseFilters (exp: string): string {
  //是否是单引号开始的字符串
  let inSingle = false
  //是否是双引号开始的字符串
  let inDouble = false
  //是否是反引号开始的字符串
  let inTemplateString = false
  //是否是正则表达式
  let inRegex = false
  //大括号处理，如果出现{，则加一，如果出现}，则减一
  let curly = 0
  //同上，只不过是处理中括号[]
  let square = 0
  //同上，只不过是处理小括号()
  let paren = 0
  //当前过滤器表达式的开始位置
  let lastFilterIndex = 0
  //对于上例，
  //expression指向'name'
  let c, prev, i, expression, filters

  for (i = 0; i < exp.length; i++) {
    prev = c
    c = exp.charCodeAt(i)

    //对于单引号、双引号、反引号和斜线来说，只是简单的跳过完整的字符串或者正则。
    //另外对prev !== 0x5C /* 反斜线\ */这个判断做一些延伸的解释：
    //对于如下模板实例：`<div>{{hello | f1('ja\\\'mes')}}</div>`
    //有这样的一段代码'ja\\\'mes'，其中\\\'在模板字符串解析时会被解析成"\'"，是语言层面的处理，
    //而之后，"\'"会在vue中被解析，也就是prev !== 0x5C判断的原因。
    //另外注意，需要\\\'这种写法的原因是包裹该字符串的字符为单引号，如果使用双引号，则不需要这么麻烦，
    //如下："ja'mes"
    if (inSingle) {
      if (c === 0x27 /* 单引号' */ && prev !== 0x5C /* 反斜线\ */) inSingle = false
    } else if (inDouble) {
      if (c === 0x22 /* 双引号" */&& prev !== 0x5C) inDouble = false
    } else if (inTemplateString) {
      if (c === 0x60 /* 反引号` */ && prev !== 0x5C) inTemplateString = false
    } else if (inRegex) {
      if (c === 0x2f /* 斜线/ */ && prev !== 0x5C) inRegex = false
    } else if (
      c === 0x7C /* 竖线| */ && // pipe
      exp.charCodeAt(i + 1) !== 0x7C && //检测是否为双竖线||
      exp.charCodeAt(i - 1) !== 0x7C &&
      !curly && !square && !paren //竖线|不在大括号、中括号或者小括号内
    ) { //在该方法中，真正需要特别处理的字符只有竖线|，因为竖线用于分割表达式与过滤器
      if (expression === undefined) { //对于第一个'|'字符，之前的是解析并设置表达式
        // first filter, end of expression
        lastFilterIndex = i + 1
        expression = exp.slice(0, i).trim()
      } else { //之后的'|'字符，都是过滤器
        pushFilter()
      }
    } else {
      switch (c) {
        case 0x22: inDouble = true; break         // "
        case 0x27: inSingle = true; break         // '
        case 0x60: inTemplateString = true; break // `
        case 0x28: paren++; break                 // (
        case 0x29: paren--; break                 // )
        case 0x5B: square++; break                // [
        case 0x5D: square--; break                // ]
        case 0x7B: curly++; break                 // {
        case 0x7D: curly--; break                 // }
      }
      //处理正则表达式
      if (c === 0x2f) { // /
        let j = i - 1
        let p
        // find first non-whitespace prev char
        //注意，这里是在向前查找字符，找到第一个不为空的字符
        for (; j >= 0; j--) {
          p = exp.charAt(j)
          if (p !== ' ') break
        }
        //判断可能存在的正则表达式之前，是否存在不合法的字符，如果存在，则
        //说明当前'/'字符不是正则表达式的开始字符
        if (!p || !validDivisionCharRE.test(p)) {
          inRegex = true
        }
      }
    }
  }

  //如果表达式不存在，则说明不存在过滤器，整个字符串都是表达式
  if (expression === undefined) {
    expression = exp.slice(0, i).trim()
  } else if (lastFilterIndex !== 0) { //感觉lastFilterIndex !== 0的判断不需要
    pushFilter() //将最后一个竖线|之后的过滤器添加到过滤器数组中
  }

  function pushFilter () {
    (filters || (filters = [])).push(exp.slice(lastFilterIndex, i).trim())
    lastFilterIndex = i + 1
  }

  if (filters) {
    for (i = 0; i < filters.length; i++) {
      expression = wrapFilter(expression, filters[i])
    }
  }

  return expression
}

function wrapFilter (exp: string, filter: string): string {
  const i = filter.indexOf('(')
  if (i < 0) {
    // _f: resolveFilter，该方法用于解析获得目标过滤器
    return `_f("${filter}")(${exp})`
  } else {
    const name = filter.slice(0, i)
    const args = filter.slice(i + 1)
    return `_f("${name}")(${exp},${args}`
  }
}
